#include "../Core/DVBO.h"
#include "../Core/Renderer.h"
#include "../Debug/Logger.h"

namespace epix {

DVBO::DVBO() :
  _index(NULL), _posV(NULL), _texV(NULL), _norV(NULL), _mode(GL_TRIANGLES)
{
}

DVBO::~DVBO()
{
	clear();
}

void DVBO::initialize(GLenum mode)
{
	_mode = mode;
}

void DVBO::addBuffer(GLenum type, const void *data, GLsizeiptr size, int count)
{
	switch(type)
    {
        case GL_VERTEX_ARRAY:
		{
			if(!data) break;
            if(_posV) delete _posV;
            _posV = new VertexBuffer(size);
            _posV->fill(size, data, GL_VERTEX_ARRAY, count);
            break;   
        }
        case GL_TEXTURE_COORD_ARRAY:
        {
			if(!data) break;
            if(_texV) delete _texV;
            _texV = new VertexBuffer(size);
			_texV->fill(size, data, GL_TEXTURE_COORD_ARRAY, count);
            break;
        }
        case GL_NORMAL_ARRAY:
        {
			if(!data) break;
            if(_norV) delete _norV;
            _norV = new VertexBuffer(size);
            _norV->fill(size, data, GL_NORMAL_ARRAY, count);
            break;
        }
        case GL_INDEX_ARRAY:
		{
			if(!data) break;
			if(_index) delete _index;
			_index = new IndexBuffer(size);
			_index->fill(size, data, GL_INDEX_ARRAY, count);
			break;
        }
        default: Logger::log() << "GLenum type (buffer) unknown : adding a buffer in the DVBO." << "\n";
    }
}

void DVBO::update(GLenum type, const void *data, GLsizeiptr size)
{
  switch(type)
    {
        case GL_VERTEX_ARRAY:
		{
			if(_posV) _posV->update(size, data);
			break;
        }
        case GL_TEXTURE_COORD_ARRAY:
        {
			if(_texV) _texV->update(size, data);
			break;
        }
        case GL_NORMAL_ARRAY:
        {
			if(_norV) _norV->update(size, data);
			break;
        }
        case GL_INDEX_ARRAY:
        {
			if(_index) _index->update(size, data);
			break;
        }
        default: Logger::log() << "GLenum type (buffer) unknown : adding a buffer in the DVBO." << "\n";
    }
}

void DVBO::bind()
{
	Renderer *rdr = Renderer::instance();
	if(_texV) rdr->setVertexBuffer(_texV);
    if(_norV) rdr->setVertexBuffer(_norV);
    if(_posV) rdr->setVertexBuffer(_posV);
    if(_index)
        rdr->setIndexBuffer(_index);
}

void DVBO::unBind()
{
	Renderer *rdr = Renderer::instance();
    rdr->setVertexBuffer(NULL);
    rdr->setIndexBuffer(NULL);
}
		
void DVBO::draw(unsigned int count)
{
    Renderer *rdr = Renderer::instance();

    if(_texV) rdr->setVertexBuffer(_texV);
    if(_norV) rdr->setVertexBuffer(_norV);
    if(_posV) rdr->setVertexBuffer(_posV);
    if(_index)
    {
        rdr->setIndexBuffer(_index);
        rdr->drawIndexedPrimitives(_mode, count, 0);
    }
    else
        rdr->drawPrimitives(_mode, count, 0);
    rdr->setVertexBuffer(NULL);
    rdr->setIndexBuffer(NULL);
}

void DVBO::drawRange(unsigned int count, unsigned int first)
{
    if(_index)
        Renderer::instance()->drawIndexedPrimitives(_mode, count, first);
    else
        Renderer::instance()->drawPrimitives(_mode, count, first);
}

void DVBO::clear()
{
	if(_posV) delete _posV;
	_posV = NULL;
    if(_texV) delete _texV;
    _texV = NULL;
    if(_norV) delete _norV;
    _norV = NULL;
    if(_index) delete _index;
    _index = NULL;
}

}
